#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <pthread.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include "wrap.h"


int main(){
    struct sockaddr_in server_sockaddr;

    //memset(&server_scokaddr,0,sizeof(server_sockaddr)); //将结构地址清0
    bzero(&server_sockaddr,sizeof(server_sockaddr));

    int s_fd = Socket(AF_INET, SOCK_STREAM, 0);
    server_sockaddr.sin_family = AF_INET;//IPv4
    server_sockaddr.sin_port = htons(7777);//端口
    server_sockaddr.sin_addr.s_addr = INADDR_ANY;//本主机的任意IP都可以使用

    //端口复用 解决服务端关闭等待2MSL期间内端口被占用的问题
    int opt = 1;
    setsockopt(s_fd,SOL_SOCKET,SO_REUSEPORT,(void*)&opt,sizeof(opt));

    Bind(s_fd,(struct sockaddr *)&server_sockaddr,sizeof(struct sockaddr));
    printf("bind success!\n");

    Listen(s_fd,128);
    printf("listen success!\n");

    fd_set rset,allset; /* rset 读事件文件描述符集合 all用于暂存  */


    int maxfd = s_fd;

    FD_ZERO(&allset); /* 构造select监控文件描述符集 */
    FD_SET(s_fd,&allset); /* 构造select监控文件描述符集合 */

    struct timeval tv;
    /* 设置超时时间为5s */
    tv.tv_sec = 50;
    tv.tv_usec = 1000;

    char buf[1024];
    while(1){
        rset = allset; //每次循环时都重新设置select监控信号集
        int nready = select(maxfd+1,&rset,NULL,NULL,NULL);
        if (nready < 0){
            perr_exit("select error");
        }

        if (FD_ISSET(s_fd,&rset)){ //说明有新的客户端请求
            struct sockaddr_in client_addr;
            int client_addr_len = sizeof(client_addr);
            int connfd = Accept(s_fd,(struct sockaddr *)&client_addr,&client_addr_len);

            FD_SET(connfd,&allset); //向监控文件描述符集合allset添加新的connfd

            if(maxfd < connfd){
                maxfd = connfd;
            }

            if(0 == --nready){  //只有s_fd有事件，后续的for不用执行了
                continue;
            }
        }

        int i,j,n;
        for(i= s_fd+1; i<=maxfd;i++){
            if(FD_ISSET(i,&rset)){ //检测哪个client有数据就绪
                if((n = Read(i,buf,sizeof(buf)))==0){//当检测到客户端关 闭时服务端也关闭
                    Close(i);
                    FD_CLR(i,&allset);//移除select对文件描述符的监控
                }else if (n > 0){
                    for (j = 0; j< n ; j++){
                        buf[j] = toupper(buf[j]);
                    }
                    Write(i,buf,n);

                }
            }
        }


    }
    return 0;
}
